﻿using System;
using System.Collections;
using Koroutine.Runtime.Command;
using Koroutine.Runtime.Condition;
using Koroutine.Runtime.Core;
using ProjectDanmaku.Broadcast;
using ProjectDanmaku.Core;
using ProjectDanmaku.Entity.SubLayer.EnemyBullet;
using ProjectDanmaku.Scenario;
using ProjectDanmaku.UI;
using ProjectDanmaku.UI.EnemyState;
using ProjectDanmaku.Util;
using UnityEngine;
using UnityEngine.Serialization;
using Random = System.Random;

namespace ProjectDanmaku.Enemy.Boss.Nightbug
{
    public partial class BossNightbugController : EnemyBehavior
    {
        private Animator _animator;
        
        private Random _random = new Random();
        
        [SerializeField] private GameObject creation0Prefab;
        
        [SerializeField] private SimpleBulletSubLayer typeCyanBulletLayer;
        [SerializeField] private SimpleBulletSubLayer typeRedBulletLayer;
        [SerializeField] private SimpleBulletSubLayer creation0BulletLayer;
        [SerializeField] private SimpleBulletSubLayer redBulletLayer;
        
        [SerializeField] private GameObject explodePrefab;
        [SerializeField] private GameObject explodeSoundPrefab;

        [SerializeField] private EnemyStateHud enemyStateHud;
        
        private float _stagePower = 0;
        
        private CoroutineScheduler _bossCoroutineScheduler;
        private CoroutineExecutionHandle _bossCoroutineHandle;

        private bool _running = true;
        private bool _hitable = false;

        private void Awake()
        {
            _animator = GetComponent<Animator>();
            _bossCoroutineScheduler = new CoroutineScheduler();
        }

        private void Start()
        {
            typeCyanBulletLayer.SetCollideLayerMask(BitUtility.EverythingExcept(1));
            typeRedBulletLayer.SetCollideLayerMask(BitUtility.EverythingExcept(1));
            creation0BulletLayer.SetCollideLayerMask(BitUtility.EverythingExcept(1));
        }

        private IEnumerator ChargePowerAnim(float power, float duration = 1f)
        {
            _stagePower = 0;
            
            const float interval = 1 / 60f;
            var inc = power / (duration / interval);

            while (_stagePower < power)
            {
                _stagePower += inc;
                yield return new DelayForSecondRealtime(interval);
            }

            _stagePower = power;
        }

        private IEnumerator MainCoroutine0()
        {
            Vector3 pos;
            
            yield return new DelayForSecond(1f);
            
            yield return new CoverCoroutine(Stage1Coroutine());
            
            pos = transform.position;
            KillAllCreations();
            yield return new CoverCoroutine(
                RaisingBulletDeathZone(pos.x, pos.y, 10, 1f));
            
            yield return new CoverCoroutine(Stage0Coroutine());
            
            pos = transform.position;
            yield return new CoverCoroutine(
                RaisingBulletDeathZone(pos.x, pos.y, 10, 1f));
            
            yield return new CoverCoroutine(Stage2Coroutine());
            
            pos = transform.position;
            yield return new CoverCoroutine(
                RaisingBulletDeathZone(pos.x, pos.y, 10, 1f));

            yield return new DelayForSecondRealtime(1f);
            
            yield return new CoverCoroutine(
                MoveToCoroutine(CoordUtility.GetPosFromCoord(new Vector2(8, 6))));
            
            yield return new DelayForSecondRealtime(1f);
            
            _running = false;
        }

        public override IEnumerator MainCoroutine()
        {
            _bossCoroutineHandle = _bossCoroutineScheduler.Schedule(MainCoroutine0());
            
            while (_running)
            {
                enemyStateHud.BloodBar.SetProgress(_stagePower / 500f);
                _bossCoroutineScheduler.Update();
                yield return null;
            }
        }
        
        private float AnimCurve(float t)
        {
            return Mathf.Pow(t, 0.333f);
        }

        private IEnumerator MoveToCoroutine(Vector3 destination)
        {
            var oPos = transform.parent.position;
            float tick = 0;

            while (tick <= 1)
            {
                transform.parent.position = Vector3.Lerp(oPos, destination, AnimCurve(tick));
                
                yield return new DelayForSecond(1 / 60f);
                
                tick += 1 / 60f;
            }
        }

        private void KillAllCreations()
        {
            foreach (var creation0 in transform.parent.GetComponentsInChildren<BossNightbugCreation0>())
            {
                creation0.PrepareDestroy();
            }
        }

        private IEnumerator Stage2Coroutine()
        {
            _hitable = false;
            yield return new DelayForSecondRealtime(1f);
            yield return new CoverCoroutine(
                MoveToCoroutine(
                    CoordUtility.GetPosFromCoord(new (8f, 7.6f))));
            yield return new CoverCoroutine(ChargePowerAnim(500));
            _hitable = true;
            
            typeRedBulletLayer.EntityLogicOverride = new BossNightbugBulletLogicLType(90);
            typeCyanBulletLayer.EntityLogicOverride = new BossNightbugBulletLogicLType(-90);

            int degBegin;
            int[] degBegins = new [] { 0, 120, 240 };

            int baseDeg = 2;
            
            for (int j = 0; j < 180; ++j)
            {
                var pos = MathHelper.Vec3ToVec2(transform.position);
            
                for (int i = 0; i < degBegins.Length; i++)
                {
                    degBegin = degBegins[i];

                    for (int deg = degBegin; deg <= degBegin + 120; deg += 6)
                    {
                        typeRedBulletLayer.Shoot(pos, 0.7f, baseDeg + deg);
                    }

                    degBegin = degBegins[degBegins.Length - i - 1];

                    for (int deg = degBegin; deg <= degBegin + 120; deg += 6)
                    {
                        typeCyanBulletLayer.Shoot(pos, 0.7f, baseDeg + deg);
                    }

                    yield return new DelayForSecond(0.03f);
                }

                baseDeg += 13;

                if (j == 30)
                {
                    yield return new CoverCoroutine(
                        MoveToCoroutine(
                            CoordUtility.GetPosFromCoord(new (2.8f, 7.2f))));
                }
                else if (j == 60)
                {
                    yield return new CoverCoroutine(
                        MoveToCoroutine(
                            CoordUtility.GetPosFromCoord(new (12.8f, 7.19f))));
                }
                else if (j == 90)
                {
                    yield return new CoverCoroutine(
                        MoveToCoroutine(
                            CoordUtility.GetPosFromCoord(new (2.86f, 6.76f))));
                }
                else if (j == 120)
                {
                    yield return new CoverCoroutine(
                        MoveToCoroutine(
                            CoordUtility.GetPosFromCoord(new (12.7f, 6.6f))));
                }
                else if (j == 150)
                {
                    yield return new CoverCoroutine(
                        MoveToCoroutine(
                            CoordUtility.GetPosFromCoord(new (7.86f, 6.36f))));
                }
                else
                {
                    yield return new DelayForSecond(0.1f);
                }
            }

            yield break;
        }
    
        private IEnumerator Stage1Coroutine()
        {
            _hitable = false;
            yield return new CoverCoroutine(
                MoveToCoroutine(CoordUtility.GetPosFromCoord(new Vector2(8, 6))));
            yield return new DelayForSecondRealtime(1f);
            yield return new CoverCoroutine(ChargePowerAnim(500));
            _hitable = true;

            for (;;)
            for (int x = 0; x < 5; x++)
            {
                var deg = _random.Next(0, 360);
                var sign = Mathf.Pow(-1, x + 1);

                Vector2[] dirs =
                {
                    MathHelper.RotateVec2(new Vector2(0, 1), deg),
                    MathHelper.RotateVec2(new Vector2(0, 1), deg + 90),
                    MathHelper.RotateVec2(new Vector2(0, 1), deg + 180),
                    MathHelper.RotateVec2(new Vector2(0, 1), deg + 270)
                };

                var pos = MathHelper.Vec3ToVec2(transform.parent.position);
                
                for (int i = 1; i < 20; ++i)
                {
                    for (int j = 0; j < dirs.Length; ++j)
                    {
                        var dir = dirs[j];
                        var norm = MathHelper.RotateVec2(dir, sign * 90);

                        var bPos = pos + dir * (i * 0.3f);
                        
                        if (CoordUtility.CheckOutOfScreen(bPos))
                            break;

                        var creation = Instantiate(creation0Prefab, transform.parent);
                        creation.GetComponent<BossNightbugCreation0>().bulletLayer = creation0BulletLayer;
                        creation.transform.position = bPos;

                        dirs[j] += norm * 0.2f;
                    }

                    yield return new DelayForSecond(0.3f);
                }

                yield return new DelayForSecond(2f);
            }
        }

        private IEnumerator Stage0Coroutine()
        {
            typeCyanBulletLayer.EntityLogicOverride = null;
            typeRedBulletLayer.EntityLogicOverride = null;

            _hitable = false;
            
            yield return new CoverCoroutine(
                MoveToCoroutine(
                    CoordUtility.GetPosFromCoord(new (8f, 7f))));
            yield return new DelayForSecondRealtime(0.5f);
            yield return new CoverCoroutine(ChargePowerAnim(500));

            _hitable = true;
            
            int startDeg = 0;

            for (;;)
            {
                for (int i = startDeg; i < 365 + startDeg; i += 5)
                {
                    typeCyanBulletLayer.Shoot(transform.parent.position, 2, i);
                }
                yield return new DelayForSecond(0.2f);
                
                for (int i = startDeg + 3; i < 368 + startDeg; i += 5)
                {
                    typeRedBulletLayer.Shoot(transform.parent.position, 1, i);

                }
                yield return new DelayForSecond(0.2f);

                startDeg += 2;
            }
        }

        protected IEnumerator RaisingBulletDeathZone(float x, float y, float radius, float duration)
        {
            Debug.Log("RaisingBulletDeathZone: Start");
            
            DanmakuScenario.BulletDeathZone.z = 0;
            DanmakuScenario.SetBulletDeathZoneState(true);
            
            DanmakuScenario.BulletDeathZone.x = x; 
            DanmakuScenario.BulletDeathZone.y = y; 
                
            var radiusRaising = Mathf.Min(radius, radius / (duration / 0.04f));
            var curRadius = 0f;
            
            for (;;)
            {
                curRadius += radiusRaising;
                DanmakuScenario.BulletDeathZone.z = curRadius;
                
                if (curRadius >= radius)
                    break;
                
                yield return new DelayForSecondRealtime(0.04f);
            }
            
            DanmakuScenario.SetBulletDeathZoneState(false);
        }

        public override void OnShot()
        {
            if (!_hitable)
                return;
            
            _stagePower -= 1;
            
            if (_stagePower <= 0)
            {
                BroadcastServer.Instance.Broadcast(CommonChannel.CameraShakeChannel);
                BroadcastServer.Instance.Broadcast(CommonChannel.BackgroundEffectChannel);
                
                Instantiate(explodeSoundPrefab);
                
                _bossCoroutineHandle.Break();
                _hitable = false;
            }
        }

        public override CircleCollider GetCollider()
        {
            return ColliderFromPosition(CoordUtility.PixelToUnit(45));
        }

        protected override int GetLayer()
        {
            return 2;
        }
    }
}